package com.ubb.dbms.core.operatorEvaluator;

import java.util.List;
import java.util.Map;

import org.json.simple.JSONObject;

import com.ubb.dbms.core.Utils.SQLExceptions.IndexKeyNullException;
import com.ubb.dbms.core.Utils.SQLExceptions.ItemAlreadyExistsException;
import com.ubb.dbms.core.Utils.SQLExceptions.ItemNotExistsException;
import com.ubb.dbms.core.checker.DefaultChecker;
import com.ubb.dbms.core.checker.DomainObjectChecker;
import com.ubb.dbms.core.connection.result.MessageResult;
import com.ubb.dbms.core.fileManagers.dataFileManager.DataFileManager;
import com.ubb.dbms.core.fileManagers.dataFileManager.DataFileManagerImpl;
import com.ubb.dbms.core.fileManagers.dataFileManager.DataFileParser;
import com.ubb.dbms.core.fileManagers.indexFileManager.IndexFileIO;
import com.ubb.dbms.core.fileManagers.indexFileManager.IndexFileManager;
import com.ubb.dbms.core.fileManagers.indexFileManager.IndexFileManagerImpl;
import com.ubb.dbms.core.fileManagers.indexFileManager.bPlusTree.BPlusTree;
import com.ubb.dbms.core.fileManagers.indexFileManager.bPlusTree.Node;
import com.ubb.dbms.core.operatorEvaluator.util.JsonConverter;
import com.ubb.dbms.core.systemCatalog.SystemCatalog;
import com.ubb.dbms.core.systemCatalog.SystemCatalogImpl;
import com.ubb.dbms.domain.queryData.Condition;
import com.ubb.dbms.domain.queryData.SelectElements;
import com.ubb.dbms.domain.queryData.TableRowElements;
import com.ubb.dbms.domain.queryResult.RawDataConverter;
import com.ubb.dbms.domain.queryResult.SelectResult;
import com.ubb.dbms.domain.queryResult.SelectedRow;
import com.ubb.dbms.domain.xml.Database;
import com.ubb.dbms.domain.xml.IndexFile;
import com.ubb.dbms.domain.xml.Table;

public class OperatorEvaluatorImpl implements OperatorEvaluator {

	private SystemCatalog systemCatalog = new SystemCatalogImpl();
	private DataFileManager dataFileManager = new DataFileManagerImpl();
	private IndexFileManager indexFileManager = new IndexFileManagerImpl();
	private DomainObjectChecker checker = new DefaultChecker(systemCatalog);

	@Override
	public void manageCreateDatabase(Database database)
			throws ItemAlreadyExistsException {
		if (checker.checkDatabase(database)) {
			systemCatalog.createDatabase(database);
			dataFileManager.createFolderForDatabase(database.getDatabaseName());
		}
	}

	@Override
	public void manageUseDatabase(String databaseName) {
		checker.checkUseDatabase(databaseName);
		// select DiscID from disciplines where DiscID = 2
//		SelectElements elements = new SelectElements();
//		elements.setTableNames(new String[] { "disciplines" });
//		elements.setProjections(new String[] { "DiscID" });
//		Condition condition = new Condition();
//		condition.setLeftOperand("DiscID");
//		condition.setRightOperator("2");
//		condition.setOperator(Operator.EQUAL);
//		elements.setCondition(new Condition[] { condition });
//		try {
//			manageSelect(elements, databaseName);
//		} catch (ItemNotExistsException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
	}

	@Override
	public void manageCreateTable(final Table table, String databaseName)
			throws ItemAlreadyExistsException {
		if (checker.checkTable(table, databaseName)) {
			checker.extendWithIndexFile(table);

			systemCatalog.createTable(table, databaseName);

			dataFileManager.createFileForTable(table, databaseName);

			for (IndexFile indexFile : table.getIndexFiles().getIndexFileList()) {
				indexFileManager.createFileForIndex(indexFile, table,
						databaseName);
			}
		}
	}

	public void manageCreateIndex(final IndexFile indexFile,
			final String tableName, final String usesDatabase)
			throws ItemNotExistsException {
		Table table = systemCatalog.getTable(tableName, usesDatabase);
		indexFile.setKeyLenght(String.valueOf(DefaultChecker.DEFAULT_INT_LENGTH));
		systemCatalog.createIndex(indexFile, table, usesDatabase);
		indexFileManager.createFileForIndex(indexFile, table, usesDatabase);
		dataFileManager.indexExistingTableData(indexFile, table, usesDatabase,
				indexFileManager);
	}

	@Override
	public void manageInsertRow(TableRowElements rowElements, String tableName,
			String usesDatabase) throws ItemNotExistsException,
			IndexKeyNullException {
		if (checker.checkUseDatabase(usesDatabase)) {
			Table table = systemCatalog.getTable(tableName, usesDatabase);
			if (checker.checkRowElements(rowElements, table)
					&& checker.checkDuplicateKey(rowElements, table,
							usesDatabase)) {
				int dataFileRowNr = dataFileManager.insertRow(rowElements,
						table, usesDatabase);
				for (IndexFile indexFile : table.getIndexFiles()
						.getIndexFileList()) {
					String keyColumnName = indexFile.getIndexAttribute()
							.getIndexAttributeList().get(0).getValue();
					Object key = rowElements.getColumnValue(keyColumnName);
					if (key != null) {
						indexFileManager.insertKey(key, dataFileRowNr,
								usesDatabase, table, indexFile);
					} else {
						throw new IndexKeyNullException(keyColumnName);
					}
				}
			}
		}
	}

	@Override
	public SelectResult manageSelect(SelectElements selectElements,
			String usesDatabase) throws ItemNotExistsException {
		Table table = systemCatalog.getTable(selectElements.getTableNames()[0],
				usesDatabase);
		List<IndexFile> indexFiles = table.getIndexFiles().getIndexFileList();

		SelectResult selectResult = new SelectResult();
		if (selectElements.getCondition() == null || selectElements.getCondition().length <= 1) {
			String conditionLeftOperand = selectElements.getCondition() == null ? null : selectElements.getCondition()[0].getLeftOperand().toString();
			IndexFile indexFile = getIndexFile(indexFiles,
					conditionLeftOperand);
			if (indexFile == null) {
				selectResult = getDataWithConditionWithProjection(
						table, usesDatabase, selectElements.getCondition() == null ? null : selectElements.getCondition()[0],
						selectElements.getProjections());
			} else {
				selectResult = getDataUsingIndexFileWithConditionWithProjection(
						indexFile, table, usesDatabase,
						selectElements.getCondition()[0],
						selectElements.getProjections());
			}
		} else {
			// SelectedRow SelectedRowConver(table, byte[],
			// selectElements.getProjections());
		}
		// megkeressuk a felhasznalt indexet
		// kivalasztjuk a legjobbak (==)
		// Vagy BPluss fat vagy indexFile attributumot es egy conditiont!!!
		// TODO ... = letrehozok egy b+tree obj es utana meghivom ra a keresest
		// >
		JSONObject json = JsonConverter.getJsonFromSelectResult(selectResult);
		MessageResult.setMessage(json);
		return null;
	}

	private SelectResult getDataUsingIndexFileWithConditionWithProjection(
			IndexFile indexFile, Table table, String usesDatabase,
			Condition condition, String[] projections) {
		// projection = [id, name] when Select id, name or *

		Map<String, String> projectionsMap= RawDataConverter.projectionArrayToMap(projections);
		// String keyColumnName = "";
		String indexFileFolder = "";
		String indexFilePath = "";
		String rsIndexFilePath = "";
		// String primaryKeyColumn =
		// table.getPrimaryKey().getPkAttribute().get(0).getValue();

		indexFileFolder = IndexFileManagerImpl.getIndexFileFolder(usesDatabase,
				table.getTableName());
		indexFilePath = IndexFileManagerImpl.getIndexFilePath(indexFileFolder,
				indexFile);
		rsIndexFilePath = IndexFileManagerImpl.getRsIndexFilePath(
				indexFileFolder, indexFile);

		IndexFileIO indexFileIO = new IndexFileIO(indexFilePath,
				rsIndexFilePath, IndexFileManager.HEADER_LENGTH,
				IndexFileManagerImpl.getIndexRowLength());
		int rootNodeRowNr = indexFileIO.readFileHeader();
		BPlusTree bPlusTree;
		if (rootNodeRowNr == 0) {
			bPlusTree = new BPlusTree(indexFileIO);
		} else {
			Node rootNode = indexFileIO.readRow(rootNodeRowNr);
			bPlusTree = new BPlusTree(rootNode, indexFileIO);
		}

		int right = Integer.parseInt(condition.getRightOperand().toString());
		Node selectResultNode = bPlusTree.getLeafNodeForKey(right);
		//Node selectResultNode = bPlusTree.searchForNode(right);
		boolean first = true;
		SelectResult result = new SelectResult();
		while (first || (selectResultNode != null
				&& selectResultNode.getmNumKey(0) == right)) {
			first = false;
			int i = 0;
			
			while (selectResultNode.getmNumKey(i) != right) {
				i++;
			}
			while (selectResultNode.getmNumKey(i) == right && i < IndexFileManager.T * 2) {
				//megkeresni a datafileba a nodehoz tartozo adatokat
				int dataFilePointer = Integer.parseInt(selectResultNode.getmObject(i).toString());
				SelectedRow row = dataFileManager.readRow(table, usesDatabase,dataFilePointer, projectionsMap);
				result.addSelectedRow(row);
				i++; 
			}
			selectResultNode = selectResultNode.getmNextNode();
		}

		return result;
	}

	private SelectResult getDataWithConditionWithProjection(Table table,
			String usesDatabase, Condition condition, String[] projections) {
		
		Map<String, String> projectionsMap = RawDataConverter.projectionArrayToMap(projections);
		DataFileParser dataFileParser = new DataFileParser(table, usesDatabase, projectionsMap);
		SelectResult selectResult = new SelectResult();
		
		//TODO checker again
		if (condition != null && condition.getRightOperand() instanceof String) {
			condition.setRightOperand(condition.getRightOperand().toString().replaceAll("\'$|^\'", ""));
		}
		
		while (dataFileParser.hasNext()) {
			SelectedRow selectedRow = dataFileParser.next();
			if (condition == null || selectedRow.checkCondition(condition)) {
				selectResult.addSelectedRow(selectedRow);
			}
		}
		
		return selectResult;
	}

	private IndexFile getIndexFile(List<IndexFile> indexFiles,
			String leftOperand) {
		
		if (leftOperand == null) {
			return null;
		}

		for (IndexFile indexFile : indexFiles) {
			String indexColumnName = indexFile.getIndexAttribute()
					.getIndexAttributeList().get(0).getValue();
			if (leftOperand.equalsIgnoreCase(indexColumnName)) {
				return indexFile;
			}
		}
		return null;
	}
}
